package com.iot.ammeter.service.impl.web.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.iot.ammeter.dto.web.login.LoginAndRegisterDTO;
import com.iot.ammeter.dto.web.menu.MenuDTO;
import com.iot.ammeter.dto.web.user.*;
import com.iot.ammeter.constant.Constant;
import com.iot.ammeter.entity.web.role.Role;
import com.iot.ammeter.entity.web.user.WebUser;
import com.iot.ammeter.exception.BusinessExceptions;
import com.iot.ammeter.mapper.web.provider.ProviderMapper;
import com.iot.ammeter.mapper.web.role.RoleMapper;
import com.iot.ammeter.mapper.web.user.WebUserMapper;
import com.iot.ammeter.query.web.user.WebUserQuery;
import com.iot.ammeter.service.web.user.WebUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iot.ammeter.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * web端用户表 服务实现类
 * </p>
 *
 * @author basil
 * @since 2021-09-25
 */
@Service
@Slf4j
@Transactional
public class WebUserServiceImpl extends ServiceImpl<WebUserMapper, WebUser> implements WebUserService {

    private final WebUserMapper webUserMapper;
    private final RedisUtil redisUtil;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    private final SnowFlakeIdWorker snowFlakeIdWorker;
    private final RoleMapper roleMapper;
    private final Random random;
    private final ProviderMapper providerMapper;

    /**
     * 注册用户默认密码
     */
    @Value("${user.password}")
    private String password;

    /**
     * 默认角色id 注册时使用
     */
    @Value("${role.id}")
    private String NORMAL_ROLE_ID;


    public WebUserServiceImpl(WebUserMapper webUserMapper, RedisUtil redisUtil, BCryptPasswordEncoder bCryptPasswordEncoder, SnowFlakeIdWorker snowFlakeIdWorker, RoleMapper roleMapper, Random random, ProviderMapper providerMapper) {
        this.webUserMapper = webUserMapper;
        this.redisUtil = redisUtil;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        this.snowFlakeIdWorker = snowFlakeIdWorker;
        this.roleMapper = roleMapper;
        this.random = random;
        this.providerMapper = providerMapper;
    }

    /**
     * web端用户注册
     *
     * @param registerDTO 注册email及密码
     * @return 注册成功或者失败
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String webUserRegister(LoginAndRegisterDTO registerDTO) {
        /**
         * 实体,数据校验
         */
        String emailToDb = registerDTO.getEmail().trim();
        String passwordToDb = registerDTO.getPassword().trim();
        String captcha = registerDTO.getCaptcha();
        if (StringUtils.isEmpty(captcha)) {
            throw new BusinessExceptions("验证码不能为空");
        }
        if (StringUtils.isEmpty(emailToDb)) {
            throw new BusinessExceptions("邮箱不能为空");
        }
        if (StringUtils.isEmpty(passwordToDb)) {
            throw new BusinessExceptions("密码不能为空");
        }
        if (!CheckTool.isEmail(emailToDb)) {
            throw new BusinessExceptions("邮箱格式有误");
        }
        /**
         * 验证码与redis中存放的验证码匹配，相等放行，不相等提示错误,匹配成功后，移除redis中的验证码
         */
        String redisCaptcha = (String) redisUtil.get(Constant.Redis.REGISTERED_WEB_USER + registerDTO.getEmail().trim());
        if (redisCaptcha == null) {
            throw new BusinessExceptions("验证码已过期");
        }
        if (!redisCaptcha.equals(captcha)) {
            throw new BusinessExceptions("验证码有误");
        }
        redisUtil.del(Constant.Redis.REGISTERED_WEB_USER + registerDTO.getEmail().trim());
        /**
         * 检查邮箱是否存在
         */
        if (webUserMapper.emailExists(emailToDb) != null) {
            throw new BusinessExceptions("邮箱已存在");
        }
        /**
         *  对密码进行加密
         */
        passwordToDb = bCryptPasswordEncoder.encode(passwordToDb);
        /**
         * 补全数据并插入数据库
         */
        WebUser webUserToDb = new WebUser();
        webUserToDb.setWebUserId(String.valueOf(snowFlakeIdWorker.nextId()));
        webUserToDb.setAccount(createAccount());
        webUserToDb.setPassword(passwordToDb);
        webUserToDb.setAvatar("https://tva1.sinaimg.cn/large/008i3skNgy1gvg886iqt6j60qt0g5jsn02.jpg");
        webUserToDb.setUserName("默认用户名");
        webUserToDb.setIsAdmin(Constant.WebUser.WEB_USER_IS_NOT_ADMIN);
        webUserToDb.setPhone(null);
        webUserToDb.setLandline(null);
        webUserToDb.setEmail(emailToDb);
        webUserToDb.setRemark(null);
        webUserToDb.setCreateTime(LocalDateTime.now());
        webUserToDb.setUpdateTime(LocalDateTime.now());
        webUserToDb.setIsDeleted(Constant.WebUser.WEB_USER_STATUS_NORMAL);
        webUserToDb.setRoleId(NORMAL_ROLE_ID);
        int result = webUserMapper.insert(webUserToDb);
        return result > 0 ? "注册成功" : "注册失败";
    }

    /**
     * web端用户登录
     *
     * @param loginDTO 登录email及密码
     * @return 返回令牌、角色id等信息
     */
    @Override
    public Map<String, Object> webUserLogin(LoginAndRegisterDTO loginDTO) {
        String email = loginDTO.getEmail().trim();
        String password = loginDTO.getPassword().trim();
        /**
         * 查询数据库，匹配邮箱密码
         */
        LoginAndRegisterDTO webUserFromDb = webUserMapper.selectWebUserByEmail(email);
        if (webUserFromDb == null) {
            return null;
        }
        /**
         * 匹配密码
         */
        boolean matches = bCryptPasswordEncoder.matches(password, webUserFromDb.getPassword());
        if (!matches) {
            return null;
        }

        /**
         * 获取用户信息，菜单列表，角色，用户名
         */
        WebUserLoginInfoDTO webUserLoginInfoDTO = webUserMapper.selectWebUserLoginInfoByEmail(email);
        /**
         * 处理菜单
         */
        List<MenuDTO> menuDTOList = webUserLoginInfoDTO.getMenu();
        List<MenuDTO> menuList = new ArrayList<>();
        if (menuDTOList.size() >= 1) {
            for (MenuDTO menuDto : menuDTOList) {
                if (menuDto.getParentId().equals(Constant.Menu.PARENT_ID)) {
                    menuDto.setSubMenu(findChildren(menuDTOList, menuDto));
                    menuList.add(menuDto);
                }
            }
            webUserLoginInfoDTO.setMenu(menuList);
        }


        /**
         * 创建token
         *          单位：毫秒
         *          -1，表示永不过期
         */
        Map<String, Object> claims = ClaimsUtil.webUserToClaims(webUserLoginInfoDTO);
        String token = JwtUtil.createToken(claims, Constant.TimeOfMilliSecond.DAY_ONE);
        Map<String, Object> map = new HashMap<>();
        map.put("username", webUserLoginInfoDTO.getUserName());
        map.put("webUserId", webUserLoginInfoDTO.getWebUserId());
        map.put("token", token);
        map.put("menu", menuList);
        return map;
    }

    /**
     * 修改web端用户密码
     *
     * @param passwordDto 新旧密码
     * @return 修改密码成功或者失败
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String changeWebUserPassword(PasswordDTO passwordDto) {
        String email = passwordDto.getEmail().trim();
        String oldPassword = passwordDto.getPassword().trim();
        String newPassword = passwordDto.getNewPassword().trim();
        /**
         * 数据校验
         */
        if (StringUtils.isEmpty(oldPassword) || StringUtils.isEmpty(newPassword)) {
            throw new BusinessExceptions("密码不能为空");
        }
        LoginAndRegisterDTO webUserFromDb = webUserMapper.selectWebUserByEmail(email);
        if (webUserFromDb == null) {
            throw new BusinessExceptions("用户不存在");
        }
        /**
         * 校验旧密码
         */
        boolean matches = bCryptPasswordEncoder.matches(oldPassword, webUserFromDb.getPassword());
        if (!matches) {
            throw new BusinessExceptions("密码错误");
        }
        /**
         * 更新密码
         */
        newPassword = bCryptPasswordEncoder.encode(newPassword);
        int result = webUserMapper.changeWebUserPassword(email, newPassword);
        return result > 0 ? "修改密码成功" : "修改密码失败";
    }

    /**
     * 重置web端用户密码
     *
     * @param passwordDto 邮箱及新密码
     * @return 重置web端用户密码成功或者失败提示
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String resetWebUserPassword(PasswordDTO passwordDto) {
        /**
         * 数据校验
         */
        String email = passwordDto.getEmail().trim();
        String password = passwordDto.getPassword().trim();
        String captcha = passwordDto.getCaptcha();
        if (!CheckTool.isEmail(email)) {
            throw new BusinessExceptions("邮箱格式有误");
        }
        if (StringUtils.isEmpty(email)) {
            throw new BusinessExceptions("邮箱不能为空");
        }
        if (StringUtils.isEmpty(password)) {
            throw new BusinessExceptions("密码不能为空");
        }
        if (StringUtils.isEmpty(captcha)) {
            throw new BusinessExceptions("验证码不能为空");
        }
        /**
         * 验证码与redis中的验证码匹配,匹配成功后移除redis中的验证码
         */
        String captchaFromRedis = (String) redisUtil.get(Constant.Redis.RESET_PASSWORD + email);
        if (!captchaFromRedis.equals(captcha.trim())) {
            throw new BusinessExceptions("验证码错误");
        }
        redisUtil.del(Constant.Redis.RESET_PASSWORD + email);
        /**
         * 更新密码
         */
        password = bCryptPasswordEncoder.encode(password);
        int result = webUserMapper.changeWebUserPassword(email, password);
        return result > 0 ? "重置密码成功" : "密码重置失败";
    }

    /**
     * 判断邮箱是否已被注册
     *
     * @param email 邮箱号
     * @return 返回邮箱是否已经存在
     */
    @Override
    public String emailIsExists(String email) {
        /**
         * 数据校验
         */
        if (StringUtils.isEmpty(email.trim())) {
            throw new BusinessExceptions("邮箱不能为空");
        }
        if (!CheckTool.isEmail(email.trim())) {
            throw new BusinessExceptions("邮箱格式有误");
        }
        /**
         * 查询，存在返回一个不为空的对象。反之返回一个为空的对象
         */
        WebUser webUser = webUserMapper.emailExists(email.trim());
        return webUser != null ? "邮箱已被注册" : "邮箱可用";
    }

    /**
     * 获取web端用户详细信息
     *
     * @param webUserId web端用户id
     * @return web端用户详细信息
     */
    @Override
    public WebUserDetailDTO getWebUserDetailByWebUserId(String webUserId) {
        WebUserDetailDTO webUserDetailDto = webUserMapper.selectWebUserDetailsByWebUserId(webUserId.trim());
        return webUserDetailDto;
    }

    /**
     * 获取web端用户列表
     *
     * @param page         请求页码
     * @param pageSize     每页数量
     * @param webUserQuery 查询条件
     * @return web端用户列表
     */
    @Override
    public IPage<WebUserDetailDTO> getWebUserList(int page, int pageSize, WebUserQuery webUserQuery) {

        String userName = webUserQuery.getUserName();
        String providerId = webUserQuery.getProviderId();
        String roleId = webUserQuery.getRoleId();
        if (userName != null) {
            userName = userName.trim();
        }
        if (providerId != null) {
            providerId = providerId.trim();
        }
        if (roleId != null) {
            roleId = roleId.trim();
        }
        /**
         * 创建page对象
         */
        Page<WebUserDetailDTO> webUserDetailDTOPage = new Page<>(page, pageSize);
        IPage<WebUserDetailDTO> webUserDetailDTOIPage = webUserMapper.selectPage(webUserDetailDTOPage, userName,
                providerId, roleId, webUserQuery.getIsAdmin());
        return webUserDetailDTOIPage;
    }

    /**
     * 添加web端用户
     *
     * @param webUserDTO 用户信息
     * @return 添加成功或者失败提示
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String addWebUser(WebUserDetailDTO webUserDTO) {
        /**
         * 数据校验
         */
        String userName = webUserDTO.getUserName().trim();
        String phone = webUserDTO.getPhone().trim();
        String email = webUserDTO.getEmail().trim();
        String providerId = webUserDTO.getProviderId().trim();
        String roleId = webUserDTO.getRoleId().trim();
        if (StringUtils.isEmpty(userName)) {
            throw new BusinessExceptions("用户名不能为空");
        }
        if (StringUtils.isEmpty(phone)) {
            throw new BusinessExceptions("手机号不能为空");
        }
        if (StringUtils.isEmpty(email)) {
            throw new BusinessExceptions("邮箱不能为空");
        }
        if (StringUtils.isEmpty(providerId)) {
            throw new BusinessExceptions("服务商id不能为空");
        }
        if (StringUtils.isEmpty(roleId)) {
            throw new BusinessExceptions("角色id不能为空");
        }
        if (!CheckTool.isEmail(email)) {
            throw new BusinessExceptions("邮箱格式有误");
        }
        if (!CheckTool.isPhone(phone)) {
            throw new BusinessExceptions("手机号有误");
        }
        /**
         * 判断邮箱用户是否存在
         */
        if (webUserMapper.emailExists(email) != null) {
            throw new BusinessExceptions("邮箱已存在");
        }
        Role role = roleMapper.selectById(roleId);
        String webUserToDbId = String.valueOf(snowFlakeIdWorker.nextId());
        WebUser webUserToDb = new WebUser();
        webUserToDb.setWebUserId(webUserToDbId);
        webUserToDb.setPassword(bCryptPasswordEncoder.encode(password));
        webUserToDb.setAccount(createAccount());
        webUserToDb.setAvatar("https://tva1.sinaimg.cn/large/008i3skNgy1gvg886iqt6j60qt0g5jsn02.jpg");
        webUserToDb.setUserName(userName);
        webUserToDb.setIsAdmin(Objects.equals(role.getRoleName(), "admin") ? Constant.WebUser.WEB_USER_IS_ADMIN : Constant.WebUser.WEB_USER_IS_NOT_ADMIN);
        webUserToDb.setPhone(phone);
        webUserToDb.setLandline(webUserToDb.getLandline());
        webUserToDb.setEmail(email);
        webUserToDb.setRemark(webUserToDb.getRemark());
        webUserToDb.setCreateTime(LocalDateTime.now());
        webUserToDb.setUpdateTime(LocalDateTime.now());
        webUserToDb.setIsDeleted(Constant.WebUser.WEB_USER_STATUS_NORMAL);
        webUserToDb.setRoleId(roleId);
        /**
         * 将用户插入数据库
         */
        int insert = webUserMapper.insert(webUserToDb);
        if (insert < 0) {
            //插入失败
            throw new BusinessExceptions("添加失败");
        }
        /**
         * 将用户所属服务商插入tb_web_user_provider
         * TODO: WebUserController  WebUserService WebUserDetailDTO
         */
        insert = webUserMapper.saveWebUserProvider(String.valueOf(snowFlakeIdWorker.nextId()), webUserToDbId,
                providerId, LocalDateTime.now(), LocalDateTime.now());
        return insert > 0 ? "添加用户成功" : "添加用户失败";
    }

    /**
     * 更新用户信息
     *
     * @param webUserDTO 要修改的信息
     * @return 操作结果详情
     */
    @Override
    public boolean updateWebUser(WebUserDetailDTO webUserDTO) {
        //查询数据库中的用户信息
        WebUser webUserFromDb = webUserMapper.selectById(webUserDTO.getWebUserId());
        webUserFromDb.setUpdateTime(LocalDateTime.now());
        //赋值 ,webUserDTO中属性为空的不复制
        BeanUtil.copyProperties(webUserDTO,
                webUserFromDb,
                CopyOptions.create()
                        .setIgnoreProperties(new String[]{"isAdmin", "roleId", "roleName", "providerName", "account", "webUserId"})
                        .setIgnoreNullValue(true));

        //查询web用户与服务商间联系
        WebUserAndProvider webUserAndProvider = providerMapper
                .selectWebUSerAndProvider(webUserDTO.getWebUserId());
        if (webUserAndProvider != null) {
            webUserAndProvider.setWebUserId(webUserDTO.getWebUserId());
            webUserAndProvider.setProviderId(webUserDTO.getProviderId());
            webUserAndProvider.setUpdateTime(LocalDateTime.now());
        } else {
            webUserAndProvider = new WebUserAndProvider();
            webUserAndProvider.setId(String.valueOf(snowFlakeIdWorker.nextId()));
            webUserAndProvider.setWebUserId(webUserDTO.getWebUserId());
            webUserAndProvider.setProviderId(webUserDTO.getProviderId());
            webUserAndProvider.setCreateTime(LocalDateTime.now());
        }
        providerMapper.saveOrUpdateProviderAndWebUser(webUserAndProvider);
        //保存
        int result = webUserMapper.updateById(webUserFromDb);
        return result > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateWebUserRole(WebUserRoleDTO webUserRole) {
        return webUserMapper.updateWebUserRole(webUserRole);
    }

    /**
     * 递归菜单查找子菜单
     *
     * @param menus   菜单集合
     * @param menuDto 菜单
     * @return 菜单列表
     */
    private List<MenuDTO> findChildren(List<MenuDTO> menus, MenuDTO menuDto) {
        List<MenuDTO> subMenu = new ArrayList<>();
        menus.forEach(item -> {
            if (item.getParentId().equals(menuDto.getMenuId())) {
                item.setSubMenu(findChildren(menus, item));
                subMenu.add(item);
            }
        });
        return subMenu;
    }


    /**
     * 随机生成账户：当前时间戳后4位+ 随机4位整数
     *
     * @return account
     */
    private String createAccount() {
        StringBuilder account = new StringBuilder();
        String currentTime = String.valueOf(System.currentTimeMillis());
        currentTime = currentTime.substring(currentTime.length() - 4);
        account.append(currentTime);
        int nextInt = random.nextInt(9999);
        nextInt = nextInt > 10000 ? nextInt : 10000 + nextInt;
        account.append(nextInt);
        return account.toString();
    }
}
